<?php

class activeRecord implements ArrayAccess {
	private $record = array();
	public $validateErrors;
	public $filterErrors;
	private $keyField = "";
	private $table = NULL;
	private $isNew = true;
	private $isValid = true;
	protected $registry;
	protected $relations = array();
	public $db;	
	private $columns;
	
	public function __construct() {
		$this->registry = registry::getInstance();
		$this->validateErrors = $this->registry->validateErrors;
		$this->filterErrors = $this->registry->filterErrors;

		$this->db = $this->registry["db"];

		$this->table = $this->modelName();
		
		$rs = $this->db->query("SHOW COLUMNS FROM ".$this->table);
		
		while ($row = $this->db->fetchRow()) {
			$this->columns[$row["Field"]] = $row;
			$this->record[$row["Field"]] = "";
		    if( $row["Key"] === "PRI" ) {
				$this->keyField = $row["Field"];
		    }
		}		
		
		if(empty($this->keyField)) {
		    throw new Exception( "Primary Column not found for Table: '".$this->table."'");
		}
		

	}
	
	public function __set($key, $value){
		$this->record[$key]=$value;
	}
	
	public function __get($key){
		return $this->record[$key];
	}
	
	public function __call($method, $args){
		$field = substr($method,6);
		if(substr($method,0,-(strlen($field)))=="findBy"){ 
			return $this->findBy($field, $args[0]);	
		}else{
			throw new Exception("Method (".$method.") not declarated in the Active Record.");
		}
	}
	
	private function modelName(){
		$source = get_class($this);
		if(preg_match("/([a-z])([A-Z])/", $source, $reg)){
			$source = str_replace($reg[0], $reg[1]."_".strtolower($reg[2]), $source);
		}
		return strtolower(inflector::pluralize($source));
	}	
	
	public function prepareFromArray($array){
		foreach ($array as $key => $var) {
			$this->record[$key] = $var;
		}		
	}

	public function prepareFromJSON($jsonData){
		$this->prepareFromArray(json_decode($jsonData));
	}
	
	public function jsonCrud($jsonData){
		//echo "<br><br>".$jsonData."<br>";
		$arrayData = json_decode($jsonData);
		//print_r($arrayData);
		//die();
		$primary = $this->keyField;
		if(in_array($arrayData->action,array("add","update","delete"))){
			//echo "Que hace: $arrayData->action<br>";
			if(count($arrayData->data)>0){
				foreach($arrayData->data as $data){
					if(isset($data->$primary)){ // <-- Esto no esta del todo bien creo...
						if(is_numeric($data->$primary) && $data->$primary > 0){
							//echo "Registro: ".$data->$primary."<br>";
							$this->find($data->$primary);
							$dataArray = get_object_vars($data);
							//print_r($dataArray);
							if($arrayData->action == "add" || $arrayData->action == "update"){
								$this->prepareFromArray($dataArray);
								$this->save();
							}
							if($arrayData->action == "delete"){
								$this->delete();
							}
							//echo "<br>";
						} else {
							throw new Exception("Wrong Primary Key, action failed");
						}
					} else {
						throw new Exception("Primary Key Missing, action failed");
					}
				}
			} else {
				throw new Exception("No hay nada en el data!");   
			}
		} else {
			throw new Exception("Unknow Action, process failed");
		}
	}
	
	public function create($values) {		
				
		$sql = "INSERT INTO ".$this->table.$this->db->buildArray("INSERT", $values);
		
		$rs = $this->db->query($sql);
		if (!$rs) {
			throw new Exception("SQL Error, Insert Failed");
		}
		
		return $this->db->lastId();
	}
	
	public function invalidate(){
		$this->isValid = false;
	}
	
	public function save() {
		$this->record =	$this->doFilter($this->record);
		$this->isValid = $this->validates($this->record);

		if ($this->isValid) {		
			if( $this->isNew ) {
				if(isset($this->columns["created"])){
					$this->record["created"] = date("Y-m-d H:i:s",strtotime("now"));
				}
				if(isset($this->columns["modified"])){
					$this->record["modified"] = date("Y-m-d H:i:s",strtotime("now"));
				}			
				$id = $this->create($this->record);
				$this->record[$this->keyField] = $id;
				$this->isNew = false;

				return $id;
			} else {
				return $this->update();
			}
		} else {
			return NULL;
		}
	}	
	
	public function update() {
		if( !isset( $this->record[$this->keyField] ) || empty( $this->record[$this->keyField] ) ) {
			throw new Exception( "Primary Key Missing, update failed" );
		}
		
		$key = $this->record[$this->keyField];
		
		if(isset($this->columns["modified"])){
			$this->record["modified"] = date("Y-m-d H:i:s",strtotime("now"));
		}
		
		$sql = "UPDATE ".$this->table." SET ".$this->db->buildArray("UPDATE", $this->record)." WHERE ".$this->keyField.'='.intval($key);
		$rs = $this->db->query($sql);
		if (!$rs) {
			throw new Exception("SQL Error, Update Failed");
		}
		
		return $rs;
	}
	
	public function delete(){
		if($this->isNew) return 0;
		$key = $this->record[$this->keyField];
		
		$sql = "DELETE FROM ".$this->table." WHERE ".$this->keyField.'='.intval($key);
		$rs = $this->db->query($sql);
		
		if (!$rs) {
			throw new Exception("SQL Error, Remove Failed");
		}
		
		return $rs;
	}
	
	public function isNew(){
		return $this->isNew;
	}
	
	public function find($id) { 
		
		$sql = "SELECT * FROM ".$this->table." WHERE ".$this->keyField."=".intval($id);
		$rs = $this->db->query($sql);
		$row = $this->db->fetchRow();
		
		$this->record = $row;
		$this->isNew = false;
		
		return $this->record;
	}
	
	public function findWithRelations($id){
		if(count($this->relations) == 0){
			return $this->find($id);
		}
		$sql = "SELECT ";
		$tables = array($this->table);
		$joins = array();
		$fields = array();
		foreach($this->relations as $rel){
			$tables[] = $rel['table'];
			$joins[] = $rel['type'].' JOIN '.$rel['table'].' ON( '.$this->table.'.'.$rel['localkey'].' = '.$rel['table'].'.'.$rel['key'].')';
			$fields[$rel['table']] = $rel['fields'];
		}
		$sql .= $tables[0].'.*';
		foreach($fields as $table=>$field){
			foreach($field as $real_name=>$As_name){
				$sql .= ', '.$table.'.'.$real_name.' AS '.$As_name;
			}
		}
		$sql .= ' FROM '.$this->table.' '.implode(' ',$joins).'  WHERE '.$this->table.'.'.$this->keyField."=".intval($id);
		
		$rs = $this->db->query($sql);
		$row = $this->db->fetchRow();
		
		$this->record = $row;
		$this->isNew = false;
		
		return $this->record;
		
	}
	
	public function findAllWithRelations($selFields = NULL, $order=NULL ,$limit=NULL, $extra=NULL){
		if(count($this->relations) == 0){
			return $this->find($id);
		}
		$sql = "SELECT ";
		$tables = array($this->table);
		$joins = array();
		$fields = array();
		foreach($this->relations as $rel){
			$tables[] = $rel['table'];
			$joins[] = $rel['type'].' JOIN '.$rel['table'].' ON('.$rel['localkey'].' = '.$rel['table'].'.'.$rel['key'].')';
			$fields[$rel['table']] = $rel['fields'];
		}
		$sql .= $tables[0].'.';
		if($selFields != NULL ){
			$sql .= implode(', '.$tables[0].'.',$selFields);
		}else{
			$sql .= '*';
		}
		foreach($fields as $table=>$field){
			foreach($field as $real_name=>$As_name){
				$sql .= ', '.$table.'.'.$real_name.' AS '.$As_name;
			}
		}
		$sql .= ' FROM '.$this->table.' '.implode(' ',$joins);
		if($extra) $sql.= $extra." ";
		if($order) $sql.= "ORDER BY ".$order.' ';
		if($limit) $sql.= "LIMIT ".$limit;
		
		$rs = $this->db->query($sql);
		$this->isNew = false;
		
		$rows = array();
		while($row = $this->db->fetchRow()) $rows[] = $row;
		return $rows;
		
	}
	
	
	public function findBy($field, $value) { 
		if(is_array($field)){
			$where = "";
			foreach($field as $k=>$v){
				$where .= $field[$k]."='".$this->db->sql_escape($value[$k])."' AND ";
			}
			$where = "(".substr($where,0,-5).")";
			
			$sql = "SELECT * FROM ".$this->table." WHERE ".$where." LIMIT 1";
		}else{
			$sql = "SELECT * FROM ".$this->table." WHERE ".$field."='".$this->db->sql_escape($value)."' LIMIT 1";
		}
		$rs = $this->db->query($sql);
		$row = $this->db->fetchRow();
		
		$this->record = $row;
		$this->isNew = false;
		
		return $this->record;
	}
	
	public function findAllBy($field, $value){
		$sql = "SELECT * FROM ".$this->table." WHERE ".$field."='".$value."'";
		
		$rs = $this->db->query($sql);
		
		$this->isNew = false;
		
		$rows = array();
		while($row = $this->db->fetchRow()) $rows[] = $row;
		
		return $rows;
	}
	
	public function findBySql($sql) { 
				
		$rs = $this->db->query($sql);
		$row = $this->db->fetchRow();
		
		$this->record = $row;
		$this->isNew = false;
		
		return $this->record;
	}
	
	public function findAllBySql($sql) { 
				
		$rs = $this->db->query($sql);
		$rows = array();
		while($row = $this->db->fetchRow()) $rows[] = $row;
		return $rows;
	}
	
	function findAll($fields=NULL, $order=NULL ,$limit=NULL, $extra=NULL) {
		$fields = $fields ? $fields : '*';
		$sql = "SELECT ".$fields." FROM ". $this->table." ";
		if($extra) $sql.= $extra." ";
		if($order) $sql.= "ORDER BY ".$order.' ';
		if($limit) $sql.= "LIMIT ".$limit;
		$rs = $this->db->query($sql);
		$rows = array();
		while($row = $this->db->fetchRow()) $rows[] = $row;
		return $rows;
	}
	
	public function getPrimaryKey() {
		return $this->keyField;
	}
    
	public function offsetExists( $offset ) {
		return (isset( $this->record[$offset] ) );
	}
	
	public function offsetGet( $offset ) {
		return $this->record[$offset];
	}

	public function offsetSet( $offset, $value ) {
		if( $offset == $this->keyField ) {
			throw new Exception("Primary Key can't be set or changed");
		}
		$this->record[$offset] = $value;
	}

	public function offsetUnset( $offset ) {
		if( isset( $this->record[$offset] ) ) {
			unset( $this->record[$offset] );
		}
	}
	
	public function sql_escape($msg){
		return $this->db->sql_escape($msg);
	}
	
	public function addRelation($table,$local_foreign_id, $foreign_id_field, $fields = array(), $type = "INNER"){
		$this->relations[] = array('table'=>$table, 'key' => $foreign_id_field, 'localkey'=> $local_foreign_id, 'fields' => $fields, 'type' => $type);
	}
}
?>
